package com.cloudlead.common.lang;

import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * Created by dean on 2015/8/5.
 */
public class CacheQueue<T> {
    public DelayQueue<DelayedItem<T>> queue = new DelayQueue<>();

    public CacheQueue() {
        Thread t = new Thread() {
            @Override
            public void run() {
                dameonCheckOverdueKey();
            }
        };
        t.setDaemon(true);
        t.start();
    }

    public int size() {
        return queue.size();
    }

    public boolean contains(Object o) {
        return queue.contains(new DelayedItem(o, 0));
    }

    public void put(T obj, long liveTimeInSeconds) {
        DelayedItem<T> tmpItem = new DelayedItem<>(obj, liveTimeInSeconds);
        if (queue.contains(tmpItem)) {
            queue.remove(tmpItem);
        }
        queue.put(tmpItem);
    }

    public void dameonCheckOverdueKey() {
        while (true) {
            queue.poll();
            try {
                Thread.sleep(300);
            } catch (Exception e) {
                // ignore
            }
        }
    }

    public class DelayedItem<T> implements Delayed {
        private T t;
        private long liveTimeInSeconds;
        private long removeTime;

        public DelayedItem(T t, long liveTimeInSeconds) {
            this.setT(t);
            this.liveTimeInSeconds = liveTimeInSeconds;
            this.removeTime = TimeUnit.NANOSECONDS.convert(liveTimeInSeconds, TimeUnit.SECONDS) + System.nanoTime();
        }

        @Override
        public int compareTo(Delayed o) {
            if (o == null) return 1;
            if (o == this) return 0;
            if (o instanceof DelayedItem) {
                DelayedItem<T> tmpDelayedItem = (DelayedItem<T>) o;
                if (liveTimeInSeconds > tmpDelayedItem.liveTimeInSeconds) {
                    return 1;
                } else if (liveTimeInSeconds == tmpDelayedItem.liveTimeInSeconds) {
                    return 0;
                } else {
                    return -1;
                }
            }
            long diff = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
            return diff > 0 ? 1 : diff == 0 ? 0 : -1;
        }

        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(removeTime - System.nanoTime(), unit);
        }

        public T getT() {
            return t;
        }

        public void setT(T t) {
            this.t = t;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            DelayedItem<?> that = (DelayedItem<?>) o;

            return t.equals(that.t);

        }

        @Override
        public int hashCode() {
            return t.hashCode();
        }
    }

}
